#include "ws.h"

#define TAG "ws"

#define WS_CONNECT_SUCC (1 << 0)

my_ws_t my_ws;

static esp_websocket_client_handle_t client;
static void                          websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
    switch(event_id)
    {
        case WEBSOCKET_EVENT_BEGIN:
            ESP_LOGI(TAG, "WEBSOCKET_EVENT_BEGIN");
            my_ws.is_connected = false;
            break;
        case WEBSOCKET_EVENT_CONNECTED:
            ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
            // 连接成功
            xEventGroupSetBits(my_ws.eg, WS_CONNECT_SUCC);
            my_ws.is_connected = true;
            break;
        case WEBSOCKET_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
            my_ws.is_connected = false;
            break;
        case WEBSOCKET_EVENT_DATA:
            my_ws.is_connected = true;
            // ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
            // ESP_LOGI(TAG, "Received opcode=%d", data->op_code);
            if(data->op_code == 0x2)
            {   // Opcode 0x2 indicates binary data
                // ESP_LOG_BUFFER_HEX("Received binary data", data->data_ptr, data->data_len);
                if(my_ws.bin_cb)
                {
                    my_ws.bin_cb(data->data_ptr, data->data_len);
                }
            }
            else if(data->op_code == 0x08 && data->data_len == 2)
            {
                // ESP_LOGW(TAG, "Received closed message with code=%d", 256 * data->data_ptr[0] + data->data_ptr[1]);
            }
            else if(data->op_code == 0x01)
            {
                // ESP_LOGI(TAG, "Received=%.*s\n\n", data->data_len, (char *)data->data_ptr);
                if(my_ws.text_cb)
                {
                    my_ws.text_cb((char *)data->data_ptr, data->data_len);
                }
            }

            break;
        case WEBSOCKET_EVENT_ERROR:
            ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
            my_ws.is_connected = false;
            break;
        case WEBSOCKET_EVENT_FINISH:
            ESP_LOGI(TAG, "WEBSOCKET_EVENT_FINISH");
            my_ws.is_connected = false;

            if(my_ws.close_cb)
            {
                my_ws.close_cb();
            }
            break;
    }
}

void ws_init(void (*text_cb)(char *, int),
             void (*bin_cb)(char *, int),
             void (*close_cb)(void))
{

    my_ws.eg       = xEventGroupCreate();
    my_ws.text_cb  = text_cb;
    my_ws.bin_cb   = bin_cb;
    my_ws.close_cb = close_cb;

    esp_websocket_client_config_t websocket_cfg = {
        .uri                  = my_ota.ws_url,
        .transport            = WEBSOCKET_TRANSPORT_OVER_SSL,   // 通过ssl进行连接
        .crt_bundle_attach    = esp_crt_bundle_attach,
        .reconnect_timeout_ms = 5000,   // 重连的超时时间
        .network_timeout_ms   = 5000,   // 网络超时时间
        .buffer_size          = 2 * 1024};

    client = esp_websocket_client_init(&websocket_cfg);

    esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client);

    char *authorization = NULL;
    asprintf(&authorization, "Bearer %s", my_ota.token);
    esp_websocket_client_append_header(client, "Authorization", strdup(authorization));
    esp_websocket_client_append_header(client, "Protocol-Version", "1");
    esp_websocket_client_append_header(client, "Device-Id", my_ota.device_id);
    esp_websocket_client_append_header(client, "Client-Id", my_ota.client_id);

    free(authorization);
}

/**
 * @brief Open audio channel
 *  函数会等到音频通道连接成功之后才返回
 */
void ws_open_audio_channel(void)
{
    if(client && !esp_websocket_client_is_connected(client))
    {
        esp_websocket_client_start(client);
        // 等待ws连接成功
        xEventGroupWaitBits(my_ws.eg, WS_CONNECT_SUCC, true, true, portMAX_DELAY);
    }
}

void ws_send_txt(char *txt)
{
    if(client && esp_websocket_client_is_connected(client) && txt)
    {
        esp_websocket_client_send_text(client, txt, strlen(txt), portMAX_DELAY);
    }
}

void ws_send_bin(char *bin, int len)
{
    if(client && esp_websocket_client_is_connected(client) && bin && len > 0)
    {
        esp_websocket_client_send_bin(client, bin, len, portMAX_DELAY);
    }
}

void ws_send_hello(void)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "type", "hello");
    cJSON_AddNumberToObject(root, "version", 1);
    cJSON_AddStringToObject(root, "transport", "websocket");

    cJSON *features = cJSON_CreateObject();
    cJSON_AddBoolToObject(features, "mcp", true);
    cJSON_AddItemToObject(root, "features", features);

    cJSON *params = cJSON_CreateObject();
    cJSON_AddStringToObject(params, "format", "opus");
    cJSON_AddNumberToObject(params, "sample_rate", 16000);
    cJSON_AddNumberToObject(params, "channels", 1);
    cJSON_AddNumberToObject(params, "frame_duration", 60);
    cJSON_AddItemToObject(root, "audio_params", params);

    char *hello = cJSON_PrintUnformatted(root);
    ws_send_txt(hello);
    cJSON_Delete(root);
    cJSON_free(hello);
}

void ws_send_abort(void)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "type", "abort");
    char *abort = cJSON_PrintUnformatted(root);
    ws_send_txt(abort);
    cJSON_Delete(root);
    cJSON_free(abort);
}

void ws_send_wake(void)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "type", "listen");
    cJSON_AddStringToObject(root, "state", "detect");
    cJSON_AddStringToObject(root, "text", "你好,小智");
    char *wake = cJSON_PrintUnformatted(root);
    ws_send_txt(wake);
    cJSON_Delete(root);
    cJSON_free(wake);
}

void ws_send_start_listening(void)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "type", "listen");
    cJSON_AddStringToObject(root, "state", "start");
    cJSON_AddStringToObject(root, "mode", "manual");
    char *listen = cJSON_PrintUnformatted(root);
    ws_send_txt(listen);
    cJSON_Delete(root);
    cJSON_free(listen);
}

void ws_send_stop_listening(void)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "type", "listen");
    cJSON_AddStringToObject(root, "state", "stop");
    char *listen = cJSON_PrintUnformatted(root);
    ws_send_txt(listen);
    cJSON_Delete(root);
    cJSON_free(listen);
}

void ws_send_iot_descriptor(void)
{
    MY_LOGI("iot_descriptor 发送");
    ws_send_txt(iot_get_descriptor());
}

void ws_send_iot_state(void)
{
    MY_LOGI("iot_state 状态发送");
    ws_send_txt(iot_get_state());
}
